﻿using Gasgoo.ShareLibrary.Framework._SerializerProvider;
using Gasgoo.ShareLibrary.Framework.iDataProvider;
using Gasgoo.ShareLibrary.Framework.Options;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Threading.Tasks;

namespace Gasgoo.CacheProvider.BySqlServe
{
    public class SqlServerCacheStorageProvider : IDataStorageProvider
    {
        readonly NewtonJsonSerializerSettings serializerSettings;
        readonly AdoNetOptions options;
        string connection;

        public SqlServerCacheStorageProvider(string providerName, IServiceProvider provider)
        {
            this.serializerSettings = new NewtonJsonSerializerSettings();
            this.options = provider.GetRequiredService<AdoNetOptions>();
            this.connection = this.options.GetConnection();
        }

        public async Task AddOrUpdateAsync(string key, object value, Type type)
        {
            try
            {
                // 这里初始化已经调用过 GetOrAddAsync， 所以直接Update
                string valueOfString = JsonConvert.SerializeObject(value, this.serializerSettings.Create());
                await using (SqlConnection conn = new SqlConnection(this.connection))
                {
                    await using (SqlCommand command = new SqlCommand($"update ClusterCacheTable set [value] = @value where [key] = @key", conn))
                    {
                        command.Parameters.AddRange(new SqlParameter[]
                        {
                            new SqlParameter("key",key),
                            new SqlParameter("value",valueOfString)
                        });
                        await conn.OpenAsync();
                        await command.ExecuteNonQueryAsync();
                    }
                }
            }
            catch (Exception)
            {
                throw;
            }
        }



        public async Task AddOrUpdateAsync(string grainType, string key, string keyExtension, object value, Type type)
        {
            key = this.keybuild(grainType, key, keyExtension);

            try
            {
                // 这里初始化已经调用过 GetOrAddAsync， 所以直接Update
                string valueOfString = JsonConvert.SerializeObject(value, this.serializerSettings.Create());
                await using (SqlConnection conn = new SqlConnection(this.connection))
                {
                    await using (SqlCommand command = new SqlCommand($"update ClusterCacheTable set [value] = @value where [key] = @key", conn))
                    {
                        command.Parameters.AddRange(new SqlParameter[]
                        {
                            new SqlParameter("key",key),
                            new SqlParameter("value",valueOfString)
                        });
                        await conn.OpenAsync();
                        await command.ExecuteNonQueryAsync();
                    }
                }
            }
            catch (Exception)
            {
                throw;
            }
        }

        public async Task<object> GetAsync(string grainType, string key, string keyExtension, Type type)
        {
            key = this.keybuild(grainType, key, keyExtension);
            try
            {
                var result = await this.GetAsync(key, type);
                return result.value;
            }
            catch (Exception)
            {

                throw;
            }
        }


        public async Task RemoveAsync(string grainType, string key, string keyExtension, object value)
        {
            key = this.keybuild(grainType, key, keyExtension);
            await using (SqlConnection conn = new SqlConnection(this.connection))
            {
                await using (SqlCommand command = new SqlCommand($"delete from [ClusterCacheTable] where [key] = @key", conn))
                {
                    command.Parameters.AddRange(new SqlParameter[]
                    {
                            new SqlParameter("key",key)
                    });
                    await conn.OpenAsync();
                    await command.ExecuteNonQueryAsync();
                }
            }
        }


        #region Private

        private string keybuild(string grainType, string key, string keyExtension) => $"{grainType}_{key}_{keyExtension}";

        private async Task<(bool isExists, string value)> GetAsync(string key)
        {
            await using (SqlConnection conn = new SqlConnection(this.connection))
            {
                await using (SqlCommand command = new SqlCommand($"select top 1 [value] from ClusterCacheTable where [key] = @key", conn))
                {
                    command.Parameters.AddRange(new SqlParameter[]
                    {
                            new SqlParameter("key",key)
                    });
                    await conn.OpenAsync();
                    var value = (string)(await command.ExecuteScalarAsync() ?? string.Empty);

                    if (string.IsNullOrWhiteSpace(value))
                    {
                        return (false, default(string));
                    }
                    else
                    {
                        return (true, value);
                    }
                }
            }
        }

        private async Task<(bool isExists, object value)> GetAsync(string key, Type type)
        {

            var result = await this.GetAsync(key);

            if (result.value == default(string))
            {
                return (result.isExists, GasgooUtils.DefaultForType(type));
            }

            return (result.isExists, JsonConvert.DeserializeObject(result.value, type, this.serializerSettings.Create()));
        }

        public Task<List<T>> QueryableAsync<T>(Queryable<T> queryable) where T : AbstractDataEntity
        {
            throw new NotImplementedException();
        }
        #endregion


    }
}
